/**
 * Creates right panel for Select Contigs/Pairs Columns
 */
package jpave.query_interface;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;

import util.Debug.ErrorReport;
import util.methods.StringHelpers;
import util.ui.CollapsiblePanel;
import util.ui.DisplayFloat;
import util.ui.MenuTreeNode;
import util.ui.UserPrompt;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import java.util.prefs.Preferences;
import java.util.Vector;

import jpave.dataholders.FilterContigData;
import jpave.dataholders.QueryData;
import jpave.manager.ManagerFrame;


public class FieldTab extends /*JPanel*/Tab implements ActionListener 
{
	private static final String [] ALWAYS_SELECTED = { "Seq ID" };
	
	private static final long serialVersionUID = 7539920711023954756L;
	private static final boolean debug = JPaveMain.debug;
	private static final int NAME_COLUMN_WIDTH = 250;
	private static final int DESCRIP_COLUMN_WIDTH = 400;
	public static final String ROUNDING_HEADER = "Rounding Numbers";
	public static final String ROUNDING_DESCRIPTION = "Set how floating-point numbers should be rounded.";


	public FieldTab ( JPaveFrame inFrame, MenuTreeNode contigNode, MenuTreeNode pairNode ) throws Exception
	{
		super(inFrame, null);
		sTCWdb = getParentFrame().getAssemblyList().getAssembly(0).getDBName(); 
		this.contigNode = contigNode;
		this.pairNode = pairNode;
		
		JButton btnExpandAll = new JButton("Expand All");
		btnExpandAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {	
				zoomAll(true);
			}
		});
		
		JButton btnCollapseAll = new JButton("Collapse All");
		btnCollapseAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {	
				zoomAll(false);
			}
		});
		
		JButton btnRefreshAll = new JButton("Refresh All Existing Filter Tables");
		btnRefreshAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {	
				refreshAll();
			}
		});
		
		JButton btnHelp = new JButton("Help");
		btnHelp.setBackground(ManagerFrame.HELPCOLOR);
		btnHelp.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				UserPrompt.displayHTMLResourceHelp(getParentFrame(), 
						"Column Selections", "html/viewSingleTCW/FieldTab.html");
			}
		});
		
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout ( buttonPanel, BoxLayout.X_AXIS ));
		buttonPanel.add(btnExpandAll);
		buttonPanel.add(Box.createHorizontalStrut(5));
		buttonPanel.add(btnCollapseAll);
		buttonPanel.add(Box.createHorizontalStrut(5));
		buttonPanel.add(btnRefreshAll);
		buttonPanel.add(Box.createHorizontalStrut(5));
		buttonPanel.add(btnHelp);
		buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		buttonPanel.setMaximumSize(buttonPanel.getPreferredSize());
		buttonPanel.setBackground(Color.WHITE);
		
		JLabel lblTitle = new JLabel("Choose Columns");
		Font f = lblTitle.getFont();
		lblTitle.setFont(new Font(f.getName(), f.getStyle(), f.getSize()+4));
		
		JTextArea txtDesc = new JTextArea("Select the columns to display in the 'Show All' and 'Filter' tables.\n" +
									"To update all existing query results, click the 'Refresh All Existing Tables' button above.\n" +
									"To update a specific result table, select it followed by the 'Refresh Columns' button.");
		txtDesc.setFont(new Font(f.getName(), Font.PLAIN, f.getSize()));
		txtDesc.setAlignmentX(Component.LEFT_ALIGNMENT);
		txtDesc.setEditable(false);
		txtDesc.setMaximumSize(txtDesc.getPreferredSize()); // needed to prevent vertical stretching
		
		centerPanel = new JPanel();
		
		BoxLayout tempLayout = new BoxLayout ( centerPanel, BoxLayout.Y_AXIS );
		centerPanel.setLayout(tempLayout);
		centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		centerPanel.setBackground(Color.WHITE);
		centerPanel.setAlignmentY(LEFT_ALIGNMENT);
		centerPanel.add(buttonPanel);
		centerPanel.add(Box.createVerticalStrut(20));
		centerPanel.add(lblTitle);
		centerPanel.add(txtDesc);
		centerPanel.add(Box.createVerticalStrut(20));

		// parentFrame is JPaveFrame
       	// creates the columns from FilterContigData.createContigFieldMapper
		// gets the list of numbers associated with columns that were last displayed 
		FieldMapper fields = getParentFrame().getContigListFields (false);
		createUIFromFields(centerPanel, fields, 0);
		
     	// creates the columns from QueryData.createContigPairFieldMapper();
		// gets the list of numbers associated with columns that were last displayed 
		FieldMapper fields2 = getParentFrame().getContigPairListFields ();
		createUIFromFields(centerPanel, fields2, 0);
		
		// CAS 12/22/14 getParentFrame().loadRoundingPrefs();
		
		contigNode.collapseChildren();
		pairNode.collapseChildren();
		
		
		JScrollPane scroller = new JScrollPane ( centerPanel );
		scroller.setBorder( null );
		scroller.setPreferredSize(getParentFrame().getSize());
		scroller.getVerticalScrollBar().setUnitIncrement(15);
		
		setLayout(new BoxLayout ( this, BoxLayout.Y_AXIS )); // needed for left-justification!
		add(scroller);
		
		setContigUIFromFields(fields); 
		setContigPairUIFromFields(fields2);
	}
	
	// these are defined in QueryData
	private boolean isPairsGroup(String str) {
		return str.equals(QueryData.GROUP_NAME_PAIR) || 
			   str.equals(QueryData.GROUP_NAME_BLAST ) ||
		       str.equals(QueryData.GROUP_NAME_HSR) ||
		       str.equals(QueryData.GROUP_NAME_OLAP);
	}
	
	private JPanel createFieldSelection(JCheckBox select, String description) {
   		JLabel lblDesc = new JLabel(description);
   		lblDesc.setFont(new Font(lblDesc.getFont().getName(),Font.PLAIN,lblDesc.getFont().getSize()));

   		JPanel subPanel = new JPanel();
   		subPanel.setLayout(new BoxLayout(subPanel, BoxLayout.X_AXIS));
   		subPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
   		subPanel.setBackground(Color.WHITE);
   		subPanel.add(select);
   		Dimension d = select.getPreferredSize();
   		if(d.width < NAME_COLUMN_WIDTH)
   			subPanel.add(Box.createHorizontalStrut(NAME_COLUMN_WIDTH - d.width));
   		subPanel.add(lblDesc);
   		d = lblDesc.getPreferredSize();
   		if(d.width < DESCRIP_COLUMN_WIDTH)
   			subPanel.add(Box.createHorizontalStrut(DESCRIP_COLUMN_WIDTH - d.width));
	
   		return subPanel;
	}
	
	private JCheckBox [] createGenericUIFromFields(CollapsiblePanel subPanel, String [] fieldNames, 
					String[] fieldDescriptions, MenuTreeNode newNode) {
		JCheckBox [] retVal = new JCheckBox[fieldNames.length];
		
	    	for (int j = 0;  j < fieldNames.length;  j++) {
	        	retVal[j] = new JCheckBox(fieldNames[j], false);
	        	retVal[j].setBackground(Color.WHITE);
	        	retVal[j].addActionListener(this);
	        	
	        	boolean done = false;
	        	for(int x=0; x<ALWAYS_SELECTED.length && !done; x++) {
	        		if(fieldNames[j].equals(ALWAYS_SELECTED[x])) {
	        			done = true;
	        			retVal[j].setSelected(true);
	        			retVal[j].setEnabled(false);
	        		}
	        	}
	        	subPanel.add(createFieldSelection(retVal[j], fieldDescriptions[j]));
	        	
	        	MenuTreeNode newNode2 = new MenuTreeNode(fieldNames[j], this);
       		newNode2.setVisible(retVal[j].isSelected());
       		newNode.addChild(newNode2);
       	}
    		return retVal;
	}
	
	public void updateOnStartup() {
		updateLibSelectedGroupFields(-1, -1);
	}
	
	private JPanel createTypeControlsForLibraryCount(Integer [] FieldIDs) {
		JPanel retVal = new JPanel();
		retVal.setLayout(new BoxLayout(retVal, BoxLayout.Y_AXIS));
		retVal.setAlignmentX(LEFT_ALIGNMENT);
		
		chkLibExpLevel = new JCheckBox("Counts");
		chkLibExpLevel.setSelected(false);
		chkLibExpLevel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				updateLibSelectedGroupFields(FilterContigData.LIBRARY_COUNT_ALL, 
						FilterContigData.N_LIBRARY_COUNT_ALL);
			}
		});
		retVal.add(chkLibExpLevel);
		
		chkLibNExpLevel = new JCheckBox("RPKM Normalized Counts");
		chkLibNExpLevel.setSelected(false);
		chkLibNExpLevel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				updateLibSelectedGroupFields(FilterContigData.N_LIBRARY_COUNT_ALL, 
						FilterContigData.CONTIG_SET_COUNT);
			}
		});
		retVal.add(chkLibNExpLevel);
		
		retVal.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		
		return retVal;
	}
	
	private void selectAllLibHit() {
		bIsLibSelectAll = chkLibSelAll.isSelected();
		
		for(int x=0; x<libMasterList.length; x++)
			libMasterList[x].setSelected(bIsLibSelectAll);
		updateFields();
	}
	
	private void selectIncLibHit() {
		bIsLibSelectInc = chkLibSelInc.isSelected();
		String [] libNames = getParentFrame().getQueryTab().getSelectedIncludeLibs();
		if(libNames == null) return;
		for(int x=0; x<libMasterList.length; x++) {
			for(int y=0; y < libNames.length; y++) {
				if(libNames[y].equals(libMasterList[x].getText()))
					libMasterList[x].setSelected(bIsLibSelectInc);
			}
		}
		updateFields();
	}
	
	private void selectExLibHit() {
		bIsLibSelectEx = chkLibSelEx.isSelected();
		String [] libNames = getParentFrame().getQueryTab().getSelectedExcludeLibs();
		if(libNames == null) return;
		for(int x=0; x<libMasterList.length; x++) {
			for(int y=0; y < libNames.length; y++) {
				if(libNames[y].equals(libMasterList[x].getText()))
					libMasterList[x].setSelected(bIsLibSelectEx);
			}
		}
		updateFields();
	}
	
	public void filterListUpdated() {
		if(bIsLibSelectInc)
			selectIncLibHit();
		if(bIsLibSelectEx)
			selectExLibHit();
	}
	
	private void updateLibSelectedGroupFields(int min, int max) {
		FieldMapper fields = getParentFrame().getContigListFields(false);
		if(min >= 0 && max >= 0)
			fields.hideIDRange(min, max);
		
		getFieldsFromUI(fields);
		setContigUIFromFields(fields);
	}
	
	private JPanel createSelectControlsForLibraryCount() {
		JPanel retVal = new JPanel();
		retVal.setLayout(new BoxLayout(retVal, BoxLayout.Y_AXIS));
		
		chkLibSelAll = new JCheckBox("check/uncheck all");
		chkLibSelAll.setSelected(bIsLibSelectAll);
		chkLibSelAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				selectAllLibHit();
			}
		});
		retVal.add(chkLibSelAll);
		chkLibSelInc = new JCheckBox("check/uncheck included");
		chkLibSelInc.setSelected(bIsLibSelectInc);
		chkLibSelInc.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				selectIncLibHit();
			}
		});
		retVal.add(chkLibSelInc);
		chkLibSelEx = new JCheckBox("check/uncheck excluded");
		chkLibSelEx.setSelected(bIsLibSelectEx);
		chkLibSelEx.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				selectExLibHit();
			}
		});
		retVal.add(chkLibSelEx);
		
		retVal.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		return retVal;
	}
	
	private void clearAllSelectControlsForLibraryCount() {
		chkLibSelAll.setSelected(false);
		chkLibSelInc.setSelected(false);
		chkLibSelEx.setSelected(false);
	}
	
	private JCheckBox [] createLibraryCountUIFromFields(CollapsiblePanel subPanel, 
				String [] fieldNames, Integer  [] fieldIDs, 
				String[] fieldDescriptions, MenuTreeNode newNode, int minID, int maxID) {
		subPanel.add(createTypeControlsForLibraryCount(fieldIDs));
		Vector<JCheckBox> mainList = new Vector<JCheckBox> ();
		
		JCheckBox [] retVal = new JCheckBox[fieldNames.length];
		
	    	for (int j = 0;  j < fieldNames.length;  j++) {
	    		retVal[j] = new JCheckBox(fieldNames[j], false);
	
	        	//Only allow exp level fields to be shown
	        	if(fieldIDs[j] >= minID && fieldIDs[j] < maxID) {
	        		JCheckBox tempCheck = new  JCheckBox(fieldNames[j], false);
	        		tempCheck.setBackground(Color.WHITE);
	        		tempCheck.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent e) {
							clearAllSelectControlsForLibraryCount();
							updateLibSelectedGroupFields(-1, -1);
						}
					});
	        		mainList.add(tempCheck);
	        		subPanel.add(createFieldSelection(tempCheck, fieldDescriptions[j]));
	        	}
        	
        		MenuTreeNode newNode2 = new MenuTreeNode(fieldNames[j], this);
       		newNode2.setVisible(retVal[j].isSelected());
       		newNode.addChild(newNode2);
       	}
    		libMasterList = mainList.toArray(new JCheckBox[0]);
		subPanel.add(createSelectControlsForLibraryCount());

		return retVal;
	}
	
	public void addNFoldFields(FieldMapper mapper) {
		if (mapper==null) System.out.println("Null mapper");
		else if (nFoldColumn==null) System.out.println("Null nFoldColumn");
		else nFoldColumn.addFoldColumns(mapper);
	}
	
	
	private void createUIFromFields(JPanel panel, FieldMapper fields, int tabLevel) {
		String[] groupNames = fields.getGroupNames();
		String[] groupDescriptions = fields.getGroupDescriptions();
		boolean addRounding=false;
		for (int i = 0;  i < groupNames.length;  i++) {
			//Major hack due to bad design trying to automate building this ui
			if(nFoldColumn == null && libMasterList != null) { // WN puts it immediately the lib list
				CollapsiblePanel tempPanel = new CollapsiblePanel("N Fold", "Fold difference between two selected libraries");
				nFoldColumn = new NFoldColumnSelectPanel(this, libMasterList);
				tempPanel.add(nFoldColumn);
		        centerPanel.add(new ItemPanel(tempPanel, tabLevel));
			}
			
			// Add node to menu tree
			MenuTreeNode newNode = new MenuTreeNode(groupNames[i], this);
			if(isPairsGroup(groupNames[i]))
				pairNode.addChild(newNode);
			else
				contigNode.addChild(newNode);
			
			// Create collapsible panel of check boxes
	        String[] fieldNames = fields.getFieldNamesByGroup(groupNames[i]);
	        String[] fieldDescriptions = fields.getFieldDescriptionsByGroup(groupNames[i]);
	        CollapsiblePanel subPanel = new CollapsiblePanel(groupNames[i], groupDescriptions[i]);
	     
	        if(groupNames[i].equals(FilterContigData.GROUP_NAME_CONTIG))
	        		ctgGeneralSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_LIB)) {
		        Integer[] fieldIDs = fields.getFieldIDsByGroup(groupNames[i]);
		        createLibraryCountUIFromFields(subPanel, fieldNames, fieldIDs, fieldDescriptions, newNode,
	        							FilterContigData.N_LIBRARY_COUNT_ALL, FilterContigData.CONTIG_SET_COUNT);
	        }
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_SEQ_SET)) {
	        	ctgSetSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        }
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_PVAL)) {
        		pValSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
        		
        		JCheckBox chkAllPval = new JCheckBox("Check/uncheck all"); chkAllPval.setBackground(Color.white);
        		chkAllPval.addItemListener(	
        			new ItemListener() 
        			{
        				public void itemStateChanged(ItemEvent e) 
        				{
        					JCheckBox chkall = (JCheckBox)e.getSource();
        					for (JCheckBox chk : pValSelect)
        					{
        						chk.setSelected(chkall.isSelected());
        					}
        					updateFields();
        				}
        			}
        		);
        		
        		subPanel.add(createFieldSelection(chkAllPval,""));
	        }
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_RSTAT))
	        		ctgRStatSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_CNTS))
	        		ctgDBHitSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_FIRST_BEST))
	        		ctgFirstBestHitSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_OVER_BEST))
	        		ctgOverBestHitSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(FilterContigData.GROUP_NAME_SNPORF)) {
	        		ctgSNPORFSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        		addRounding = true; // WN - another hack made necessary by this misguided effort to "automate" building the column chooser
	        }
	        else if(groupNames[i].equals(QueryData.GROUP_NAME_PAIR))
	        		ctgPairGeneralSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(QueryData.GROUP_NAME_BLAST))
	        		ctgPairBlastSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(QueryData.GROUP_NAME_HSR))
	        		ctgPairHSRSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        else if(groupNames[i].equals(QueryData.GROUP_NAME_OLAP))
	        		ctgPairOLPSelect = createGenericUIFromFields(subPanel, fieldNames, fieldDescriptions, newNode);
	        	
	        //added to create a "tab" option
	        centerPanel.add(new ItemPanel(subPanel, tabLevel));
	        
			if (roundingPanel == null && i == groupNames.length-1) {
				CollapsiblePanel tempPanel = new CollapsiblePanel(ROUNDING_HEADER, ROUNDING_DESCRIPTION);
				roundingPanel = createRoundingPanel();
				tempPanel.add(roundingPanel);
				centerPanel.add(new ItemPanel(tempPanel, tabLevel));
			}		
		}

	}
	ActionListener roundActionListener = new ActionListener() 
	{
        public void actionPerformed(ActionEvent ae) 
        {
        	updateRoundingFields(true);
        }        
    };
    DocumentListener roundTextListener = new DocumentListener() 
    {
    	public void changedUpdate(DocumentEvent e) 
    	{
    		updateRoundingTextFields(true);
    	}
    	public void removeUpdate(DocumentEvent e) 
    	{
    		updateRoundingTextFields(true);
    	}
    	public void insertUpdate(DocumentEvent e) 
    	{
    		updateRoundingTextFields(true);
    	}

    };  
    private void updateRoundingTextFields(boolean flush)
    {
	    	try
	    	{
				int sigFigs = Integer.parseInt(fldNumSigs.getText());
				if (sigFigs < 0)
				{
					System.err.println("Invalid significant digits entry:" + sigFigs);
					return;
				}
				int dec = Integer.parseInt(fldNumDec.getText());
				if (dec < 0)
				{
					System.err.println("Invalid decimal rounding entry:" + dec);
					return;
				}
				DisplayFloat.setDigits(sigFigs,dec);
				if (flush) flushRoundingPrefs();
	    	}
	    	catch(Exception e)
	    	{
	    		; // just means they're in the middle of editing
	    	}
    }
    private void updateRoundingFields(boolean flush)
    {
		int sigFigs = Integer.parseInt(fldNumSigs.getText());
		if (sigFigs < 0)
		{
			System.err.println("Invalid significant digits entry:" + sigFigs);
			return;
		}
		int dec = Integer.parseInt(fldNumDec.getText());
		if (dec < 0)
		{
			System.err.println("Invalid decimal rounding entry:" + dec);
			return;
		}
	    if (chkDecAndSig.isSelected())
	    {
	    	DisplayFloat.setMode(DisplayFloat.MODE_ROUND_DEC_PLUS_SIGFIGS, sigFigs, dec); 
	    	fldNumSigs.setEnabled(true);
	    	fldNumDec.setEnabled(true);
	    }
	    else if (chkDec.isSelected())
	    {
	    	DisplayFloat.setMode(DisplayFloat.MODE_ROUND_DEC, sigFigs, dec);  
	       	fldNumSigs.setEnabled(false);
	    	fldNumDec.setEnabled(true);
	    }
	    else if (chkSig.isSelected())
	    {
	    	DisplayFloat.setMode(DisplayFloat.MODE_ROUND_SIGFIGS, sigFigs, dec);  
	       	fldNumSigs.setEnabled(true);
	    	fldNumDec.setEnabled(false);
	    }
	    else if (chkNoRound.isSelected())
	    {
	    	DisplayFloat.setMode(DisplayFloat.MODE_NOROUND, sigFigs, dec);  
	       	fldNumSigs.setEnabled(false);
	    	fldNumDec.setEnabled(false);
	    }
	    else if (chkLegacy.isSelected())
	    {
	    	DisplayFloat.setMode(DisplayFloat.MODE_LEGACY_TRUNCATE, sigFigs, dec);  
	       	fldNumSigs.setEnabled(false);
	    	fldNumDec.setEnabled(false);
	    }
	    if (flush) flushRoundingPrefs();
    }
	private JPanel createRoundingPanel()
	{
		JPanel thePanel = new JPanel();
		thePanel.setLayout(new BoxLayout(thePanel, BoxLayout.PAGE_AXIS));
   		thePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
   		thePanel.setBackground(Color.WHITE);
   		
   		JPanel textRow = new JPanel();
   		textRow.setLayout(new BoxLayout(textRow, BoxLayout.X_AXIS));
   		textRow.setAlignmentX(Component.LEFT_ALIGNMENT);
   		textRow.setBackground(Color.WHITE);

		fldNumDec = new JTextField(String.valueOf(DisplayFloat.NUM_DEC_PLACE),3); 
		fldNumSigs = new JTextField(String.valueOf(DisplayFloat.NUM_SIG_FIGS),3);
		
		fldNumSigs.getDocument().addDocumentListener(roundTextListener);
		fldNumDec.getDocument().addDocumentListener(roundTextListener);
		
		textRow.add(new JLabel("Significant digits: "));
		textRow.add(fldNumSigs);
		textRow.add(Box.createHorizontalStrut(5));
		textRow.add(new JLabel("Decimal places: "));
		textRow.add(fldNumDec);
		textRow.add(Box.createHorizontalGlue());
		
		thePanel.add(Box.createVerticalStrut(5));
		thePanel.add(textRow);

		chkDecAndSig = new JRadioButton();chkDecAndSig.setBackground(Color.white);chkDecAndSig.addActionListener(roundActionListener);
		chkDec = new JRadioButton();chkDec.setBackground(Color.white);chkDec.addActionListener(roundActionListener);
		chkSig = new JRadioButton();chkSig.setBackground(Color.white);chkSig.addActionListener(roundActionListener);
		chkNoRound = new JRadioButton();chkNoRound.setBackground(Color.white);chkNoRound.addActionListener(roundActionListener);
		chkLegacy = new JRadioButton();chkLegacy.setBackground(Color.white);chkLegacy.addActionListener(roundActionListener);
		
		ButtonGroup bg = new ButtonGroup();
		bg.add(chkDecAndSig);
		bg.add(chkDec);
		bg.add(chkSig);
		bg.add(chkNoRound);
		bg.add(chkLegacy);
		
		if (DisplayFloat.MODE == DisplayFloat.MODE_LEGACY_TRUNCATE)
		{
			chkLegacy.setSelected(true);
		}
		else if (DisplayFloat.MODE == DisplayFloat.MODE_NOROUND)
		{
			chkNoRound.setSelected(true);
		}
		else if (DisplayFloat.MODE == DisplayFloat.MODE_ROUND_DEC)
		{
			chkDec.setSelected(true);
		}
		else if (DisplayFloat.MODE == DisplayFloat.NUM_SIG_FIGS)
		{
			chkSig.setSelected(true);
		}
		else
		{
			chkDecAndSig.setSelected(true); // default
		}
		
		thePanel.add(Box.createVerticalStrut(5));
		thePanel.add(chkboxRow(chkDecAndSig, "Round to decimal, or use significant digits for numbers < 1"));
		thePanel.add(Box.createVerticalStrut(5));
		thePanel.add(chkboxRow(chkSig, "Round to significant digits"));
		thePanel.add(Box.createVerticalStrut(5));
		thePanel.add(chkboxRow(chkDec, "Round to decimal"));
		thePanel.add(Box.createVerticalStrut(5));
		thePanel.add(chkboxRow(chkNoRound, "No rounding (show full values)"));
		thePanel.add(Box.createVerticalStrut(5));
		thePanel.add(chkboxRow(chkLegacy, "Legacy mode, truncate to decimal or significant digits for numbers < 1"));
		
		updateRoundingFields(false);
		
		return thePanel;
	}
	private JPanel chkboxRow(JRadioButton chk, String label)
	{
		JPanel row = new JPanel();
   		row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
   		row.setAlignmentX(Component.LEFT_ALIGNMENT);
   		row.setBackground(Color.WHITE);
		row.add(chk);
		row.add(Box.createHorizontalStrut(5));
		row.add(new JLabel(label));
		row.add(Box.createHorizontalGlue());
		return row;
	}
	private void setUIForGroupFromFields(JCheckBox [] chkFields, FieldMapper fields, String groupName) {
		if(chkFields == null) return;
		
		for(int x=0; x<chkFields.length; x++) {
			String name = chkFields[x].getText();
		
			if (fields.getFieldRequiredByName(name)) {
				chkFields[x].setEnabled(false);
				chkFields[x].setSelected(true);
				if(isPairsGroup(groupName))
					pairNode.getDescendentWithName(chkFields[x].getText()).setVisible(true);
				else
					contigNode.getDescendentWithName(chkFields[x].getText()).setVisible(true);
			}
			else {
				boolean selected = fields.isFieldVisible(name);
				chkFields[x].setSelected(selected);
				if(isPairsGroup(groupName))
					pairNode.getDescendentWithName(chkFields[x].getText()).setVisible(selected);
				else
					contigNode.getDescendentWithName(chkFields[x].getText()).setVisible(selected);
			}
		}
	}

	private void setUIForGroupFromFieldsForLibrary(JCheckBox [] chkFields, FieldMapper fields) {
		String [] strIDList = fields.getVisibleFieldIDsStr();
		String [] strLabelList = fields.getVisibleFieldNames();
		int [] ids = fields.getAllFieldIDs();
		String [] names = fields.getFieldNames();
		
		String [] temp = getParentFrame().getPreferencesRoot().get(ctgPrefLabel(), "").split("\t");
		Vector<String> colNames = new Vector<String> ();
		for(int x=0; x<temp.length; x++)
			colNames.add(temp[x]);
		
		if (chkLibExpLevel == null) {
			ErrorReport.die("Internal Error: null chkLibExpLevel in setUIForGroupFromFieldsForLibrary in FieldTab");
			return;
		}
		boolean expLevel = chkLibExpLevel.isSelected();
		boolean expNLevel = chkLibNExpLevel.isSelected();
		
		for(int x=0; x<ids.length; x++) {
			if(ids[x] >= FilterContigData.LIBRARY_COUNT_ALL && ids[x] < FilterContigData.CONTIG_SET_COUNT)
				contigNode.getDescendentWithName(names[x]).setVisible(false);
		}
			
		for(int x=0; x<strIDList.length; x++) {
			int id = Integer.parseInt(strIDList[x]);
			if(id >= FilterContigData.LIBRARY_COUNT_ALL && id < FilterContigData.N_LIBRARY_COUNT_ALL && 
					colNames.contains(strLabelList[x])) {
				expLevel = true;
				chkFields[id - FilterContigData.LIBRARY_COUNT_ALL].setSelected(true);
				contigNode.getDescendentWithName(strLabelList[x]).setVisible(true);
			}
			else if(id >= FilterContigData.N_LIBRARY_COUNT_ALL && id < FilterContigData.CONTIG_SET_COUNT && 
					colNames.contains(strLabelList[x])) {
				expNLevel = true;
				chkFields[id - FilterContigData.N_LIBRARY_COUNT_ALL].setSelected(true);
				contigNode.getDescendentWithName(strLabelList[x]).setVisible(true);
			}
		}

		chkLibExpLevel.setSelected(expLevel);
		chkLibNExpLevel.setSelected(expNLevel);
		
		boolean found = false;
		//Now check if nothing selected, if so, select RPKM option
		for(int x=0; x<libMasterList.length && !found; x++)
			found = libMasterList[x].isSelected();
		
		if(!chkLibExpLevel.isSelected() && !chkLibNExpLevel.isSelected() && !found)
			chkLibNExpLevel.setSelected(true);
			
		//Update "select all"
		found = true; 
		
		for(int x=0; x<libMasterList.length && found; x++)
			found = libMasterList[x].isSelected();
		
		chkLibSelAll.setSelected(found);
			
	}
	
	private void setUIForGroupFromFieldsForNFold(FieldMapper fields, String prefNameID, String prefNameLabel) {
		//Since the nFold columns do not technically exist, the previous selected columns need to be taken from the preferences
        Preferences prefs = getParentFrame().getPreferencesRoot();
        String [] names = prefs.get(ctgPrefLabel(), "").split("\t");
        String [] ids = prefs.get(ctgPrefID(), "").split(",");
        if (names.length <= 1) return;

        for(int x=0; x<ids.length; x++) {
			if(fields.isNFoldField(Integer.parseInt(ids[x])) && x<names.length) {
				nFoldColumn.addFoldColumn(names[x]);
			}
		}
	}
	
	public void resetDynamicLists() {
		if(libMasterList != null)
			for(int x=0; x<libMasterList.length; x++) {
				libMasterList[x].setSelected(false);
			}
		
		chkLibSelAll.setSelected(false);
		chkLibSelInc.setSelected(false);
		chkLibSelEx.setSelected(false);
		chkLibExpLevel.setSelected(false);
		chkLibNExpLevel.setSelected(true);
	}
	
	/******************************************************************************
	 * XXX Preferences -- prefixed by database name
	 * these are also in JPaveFrame
	 */
	private String ctgPrefID() {
		return sTCWdb + "_ctgID";
	}
	private String ctgPrefLabel() {
		return sTCWdb + "_ctgLabel";
	}
	private String roundPrefID() {
		return "rounding";		
	}
	private String pairPrefID() {
		return sTCWdb + "_pairID";
	}
	private String pairPrefLabel() {
		return sTCWdb + "_pairLabel";
	}
	public void setContigUIFromFields(FieldMapper fields) {
		String prefNameID = ctgPrefID();
		String prefNameLabel = ctgPrefLabel();
		setUIForGroupFromFields(ctgGeneralSelect, fields, FilterContigData.GROUP_NAME_CONTIG);
		setUIForGroupFromFieldsForLibrary(libMasterList, fields);
		setUIForGroupFromFields(ctgSetSelect, fields, FilterContigData.GROUP_NAME_SEQ_SET);
		setUIForGroupFromFields(pValSelect, fields, FilterContigData.GROUP_NAME_PVAL);
		setUIForGroupFromFields(ctgRStatSelect, fields, FilterContigData.GROUP_NAME_RSTAT);
		setUIForGroupFromFields(ctgDBHitSelect, fields, FilterContigData.GROUP_NAME_CNTS);
		setUIForGroupFromFields(ctgFirstBestHitSelect, fields, FilterContigData.GROUP_NAME_FIRST_BEST);
		setUIForGroupFromFields(ctgOverBestHitSelect, fields, FilterContigData.GROUP_NAME_OVER_BEST);
		setUIForGroupFromFields(ctgSNPORFSelect, fields, FilterContigData.GROUP_NAME_SNPORF);
		setUIForGroupFromFieldsForNFold(fields, prefNameID, prefNameLabel);

        setFields(fields, prefNameID, prefNameLabel);
	}
	
	public void setContigPairUIFromFields(FieldMapper fields) {		
		String prefNameID = pairPrefID();
		String prefNameLabel = pairPrefLabel();
		setUIForGroupFromFields(ctgPairGeneralSelect, fields, QueryData.GROUP_NAME_PAIR);
		setUIForGroupFromFields(ctgPairBlastSelect, fields, QueryData.GROUP_NAME_BLAST);
		setUIForGroupFromFields(ctgPairHSRSelect, fields, QueryData.GROUP_NAME_HSR);
		setUIForGroupFromFields(ctgPairOLPSelect, fields, QueryData.GROUP_NAME_OLAP);

        setFields(fields, prefNameID, prefNameLabel);
	}
	
	private void setFields(FieldMapper fields, String prefNameID, String prefNameLabel) {
		try {
	        String strLabelList = StringHelpers.join(fields.getVisibleFieldNames(), "\t");
	        String strIDList = StringHelpers.join( fields.getVisibleFieldIDsStr(), "," );
	        Preferences prefs = getParentFrame().getPreferencesRoot();
	        prefs.put ( prefNameID, strIDList );
	        prefs.flush();
	        prefs.put ( prefNameLabel, strLabelList );
	        prefs.flush();
	        flushRoundingPrefs();
		}
		catch (Exception err) {
			ErrorReport.reportError(err, "Internal error in FieldTab: setting preferences");
		}
	}
	private void flushRoundingPrefs() 
	{
		try
		{
			Preferences prefs = getParentFrame().getPreferencesRoot();
			prefs.put(roundPrefID(), DisplayFloat.getPrefString());
			prefs.flush();
		}
		catch (Exception err) {
			ErrorReport.reportError(err, "Internal error in FieldTab: setting preferences");
		}
	}
	private Vector<String> getFieldsFromUIForGroup(JCheckBox [] chkSelections, FieldMapper fields) {
		
		Vector<String> retVal = new Vector<String> ();
		
		if(chkSelections == null) return retVal;
		
		for(int x=0; x<chkSelections.length; x++) {
			String name = chkSelections[x].getText();
			if (chkSelections[x].isSelected()) {
				if (fields.hasFieldName(name)) {
					retVal.add(name);
				}
			}			
		}	
		return retVal;
	}
	
	private Vector<String> getFieldsFromUIForLibrary(JCheckBox[] chkSelections, FieldMapper fields) {
		Vector<String> retVal = new Vector<String> ();
		
		if(chkSelections == null) return retVal;
		
		for(int x=0; x<chkSelections.length; x++) {
			if(chkSelections[x].isSelected()) {
				String name = chkSelections[x].getText();
				if(chkLibExpLevel.isSelected()) retVal.add("#" + name);
				if(chkLibNExpLevel.isSelected()) retVal.add(name);
			}
		}		
		return retVal;		
	}
	
	private Vector<String> getFieldsFromUIForNFold(FieldMapper fields) {
		Vector<String> retVal = new Vector<String> ();
				
		String [] names = nFoldColumn.getColumnNames();
		for(int x=0; x<names.length; x++) {
			if(!retVal.contains(names[x]))
				retVal.add(names[x]);
		}

		return retVal;		
	}
	
	private void getFieldsFromUI(FieldMapper fields) {
		Vector<String> selected = new Vector<String>();	
				
		selected.addAll(getFieldsFromUIForGroup(ctgGeneralSelect, fields));
		selected.addAll(getFieldsFromUIForLibrary(libMasterList, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgSetSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(pValSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgRStatSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgDBHitSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgFirstBestHitSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgOverBestHitSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgSNPORFSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgPairGeneralSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgPairBlastSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgPairHSRSelect, fields));
		selected.addAll(getFieldsFromUIForGroup(ctgPairOLPSelect, fields));
		selected.addAll(getFieldsFromUIForNFold(fields));

		fields.setVisibleFieldNames(selected.toArray());
	}
	
	public String [] getFoldColumns() { return nFoldColumn.getColumnNames(); }
	public int [] getFoldIDs() { return nFoldColumn.getIDs(); }
	
	public int getFieldID(String name) { return nFoldColumn.getIDForName(name); }
	
	public void updateFields() {
		FieldMapper fields = getParentFrame().getContigListFields(false);
		FieldMapper fields2 = getParentFrame().getContigPairListFields();
		
		// Load fields from UI checkboxes
		getFieldsFromUI(fields);
		getFieldsFromUI(fields2);

		// Update menu tree and prefs file
		setContigUIFromFields(fields);
		setContigPairUIFromFields(fields2);		
	}
	// This is not very efficient, but it works for now ...
	// Could put this code before executeQuery() so it only runs once, not
	// for every individual checkbox state change.
	public void actionPerformed(ActionEvent e) {
		updateFields();
	}
	
	public void zoom(MenuTreeNode node) {
		setView(node);
		Component[] comps = centerPanel.getComponents();
		for (int i = 0;  i < centerPanel.getComponentCount();  i++) {		
			if (comps[i] instanceof ItemPanel) {
				CollapsiblePanel c = ((ItemPanel)comps[i]).getPanel();
				if (node.getText().equals(c.getTitle())) {
					c.expand();
					c.scrollRectToVisible(new Rectangle(new Point(0,0), c.getMaximumSize()));		
					break;
				}
			}
		}
	}
	
	public void zoomAll(boolean expand) {
		Component[] comps = centerPanel.getComponents();
		
		for (int i = 0;  i < centerPanel.getComponentCount();  i++) {		
			if (comps[i] instanceof ItemPanel) {
				CollapsiblePanel c = ((ItemPanel)comps[i]).getPanel();
				if (expand) c.expand();
				else c.collapse();
			}
		}
	}
	
	public void refreshAll() {
		Tab tabs[] = getParentFrame().tabbedPane.getTabs();
		for ( int i = 0; i < tabs.length; ++i ) {
			// Update any open contig list tabs
			if ( tabs[i] instanceof ContigListTab ) {
				ContigListTab tab = (ContigListTab)tabs[i];
				if (debug) System.err.println("FieldTab refreshAll contig " + tab.getTitle());
				if(tab.getQuery() != null && tab.getContigIDs() != null)
					getParentFrame().handleLoadQuery ( tab, tab.getQuery(), tab.getContigIDs(), tab.getViewMode(), null );
				else if(tab.getQuery() != null)
					getParentFrame().handleLoadQuery ( tab, tab.getQuery(), null );
			}
			// Update any open contig pair list tabs
			else if ( tabs[i] instanceof ContigPairListTab ) {
				ContigPairListTab tab = (ContigPairListTab)tabs[i];
				if (debug) System.err.println("FieldTab refreshAll pair " + tab.getTitle());
				getParentFrame().handleLoadQuery ( tab, tab.getQuery(), null );
			}
		}
	}
	
	public void setView(MenuTreeNode node) {
		
		Component[] comps = centerPanel.getComponents();
		boolean visibleFlag = true;

		if(node.equals(pairNode) || (node != null && node.getParentNode().equals(pairNode)))
			visibleFlag = false;
		
		for(int x=0; x<comps.length; x++)
			if (comps[x] instanceof ItemPanel) {
				CollapsiblePanel c = ((ItemPanel)comps[x]).getPanel();
				if (isPairsGroup(c.getTitle()))
					c.setVisible(!visibleFlag);
				else
					c.setVisible(visibleFlag);
			}
	}
	
	//Cleanup memory on exit
	public void close()
	{
		centerPanel = null;
		contigNode = null;
		pairNode = null;
	}

	//Checkboxes organized by group
	//Contig
	private JCheckBox [] ctgGeneralSelect = null;
	private JCheckBox [] ctgSetSelect = null;
	private JCheckBox [] pValSelect = null;
	private JCheckBox [] ctgRStatSelect = null;
	private JCheckBox [] ctgDBHitSelect = null;
	private JCheckBox [] ctgFirstBestHitSelect = null;
	private JCheckBox [] ctgOverBestHitSelect = null;
	private JCheckBox [] ctgSNPORFSelect = null;
	
	//Contig Pair
	private JCheckBox [] ctgPairGeneralSelect = null;
	private JCheckBox [] ctgPairBlastSelect = null;
	private JCheckBox [] ctgPairHSRSelect = null;
	private JCheckBox [] ctgPairOLPSelect = null;
	
	//viewable column selection for libraries
	private JCheckBox [] libMasterList = null;
	
	//Library selection
	private JCheckBox chkLibSelAll = null;
	private JCheckBox chkLibSelInc = null;
	private JCheckBox chkLibSelEx = null;
	private JCheckBox chkLibExpLevel = null;
	private JCheckBox chkLibNExpLevel = null;
	
	private NFoldColumnSelectPanel nFoldColumn = null;
	private JComponent roundingPanel = null;
	
	private boolean bIsLibSelectAll = false;
	private boolean bIsLibSelectInc = false;
	private boolean bIsLibSelectEx = false;
	
	//Rounding panel
	private JTextField fldNumSigs=null, fldNumDec=null;
	private JRadioButton chkDecAndSig=null, chkDec=null, chkSig=null, chkNoRound=null, chkLegacy=null;
	
	//Hold unitrans set names (name retrieval only, not filtered)
	private String [] ctgSetNameList = null;
	private String [] pValColumns = null;
	
	private JPanel centerPanel = null;
	private MenuTreeNode contigNode = null, pairNode = null;
	
	private String sTCWdb = ""; // CAS 3/31/13 used for preference prefix 
	
	private class ItemPanel extends JPanel
	{
		private static final long serialVersionUID = 7344260472768859642L;

		public ItemPanel(CollapsiblePanel cp, int tabLevel)
		{
	    		setLayout(new BoxLayout ( this, BoxLayout.X_AXIS ));
	    		setAlignmentX(Component.LEFT_ALIGNMENT);
	    		setBackground(Color.WHITE);
	    		if(tabLevel > 0)
	    			add(Box.createRigidArea(new Dimension(tabLevel, 0)));
	    		add(cPanel = cp);
		}
		
		public CollapsiblePanel getPanel() { return cPanel; }
		
		private CollapsiblePanel cPanel = null;
	}
}